package org.showminifier.servlet;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.jdo.PersistenceManager;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.*;

import org.showminifier.core.HtmlEngine;
import org.showminifier.core.MinifierException;
import org.showminifier.core.Options;
import org.showminifier.web.PMF;
import org.showminifier.web.PageContent;
import org.showminifier.web.ParserException;
import org.showminifier.web.RequestHTTPHeader;
import org.showminifier.web.Settings;
import org.showminifier.web.UrlParser;
import org.showminifier.web.Util;
import org.showminifier.web.WebContent;
import org.showminifier.web.WebContentUtil;

import com.google.appengine.api.datastore.Link;
import com.google.appengine.api.datastore.Text;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

/**
*
* Show Minifier
* Author: Shihua Ma <beijing.josh@gmail.com>
* Copyright (c) 2010 Shihua Ma
* Licensed under the MIT license.
*
*/

@SuppressWarnings("serial")
public class HtmlMinifyServlet extends HttpServlet {
	
	private static final Logger log = Logger.getLogger(HtmlMinifyServlet.class.getName());
	
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException, ServletException {
				
		UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();
        String lable = req.getParameter("lable");
        String url = req.getParameter("url");
        String owner = req.getParameter("owner");
        String optionsSetting = req.getParameter("options-setting");

        if(url != null){
        	url = url.trim();
        }
        //valid input url
        if(url == null || !Util.isValidURL(url)){
        	dispatchToErrorPage(req,resp,"The url \"" + url + "\" is not a valid url.");
        	return;
        }
        
        //parser request headers
        ArrayList<RequestHTTPHeader> requestHeaders = new ArrayList<RequestHTTPHeader>();
		Enumeration headerNames = req.getHeaderNames();
		while(headerNames.hasMoreElements()){
			String headerName = (String)headerNames.nextElement();
			if(headerName != null && 
					! ("cookie".equalsIgnoreCase(headerName) ||
							headerName.equalsIgnoreCase("host") || "referer".equalsIgnoreCase(headerName)))
				requestHeaders.add(new RequestHTTPHeader(headerName,req.getHeader(headerName)));
		}
		
		WebContent content = new WebContent();
		content.setRequestUser(user);
		content.setLable(lable == null ? "" : lable);
		content.setUrl(new Link(url));
		content.setRequestDate(new Date());
		content.setIpAddress(Util.getClientAddress(req));
		//options
		Options options = null;
		//Options 
		if(user != null && owner!= null && owner.equals("true")){			
			Settings settings = Util.getUserSetting(user);
			if(settings != null){
				options = settings.getOptions();
			}else{
				options = Util.buildOptions(req, false);
			}
		}else{
			if(optionsSetting != null ){
				if(optionsSetting.equalsIgnoreCase("all-options")){
					options = Util.buildOptions(req, true);
				}else if(optionsSetting.equalsIgnoreCase("custom-options")){
					options = Util.buildOptions(req, false);
				}else if(optionsSetting.equalsIgnoreCase("default-options") ){
					options = Util.defuultOptions();
				}
			}else{
				options = Util.defuultOptions();
			}
			
		}
		URL parserURL = new URL(url);
		options.setUrlCheme(parserURL.getProtocol()+"://");
		content.setOptions(options);
		//parser html
		UrlParser parser = new UrlParser(parserURL,requestHeaders);
		try{
			parser.parser();
		}catch(ParserException e){
			dispatchToErrorPage(req,resp,e.toString());
		}
		//page content
		if(parser.getResponseCode() == HttpURLConnection.HTTP_OK){
			PageContent page = new PageContent();
			content.setPage(page);
			String html = parser.getContent();
			page.setOriginalContent(new Text(html));
			page.setRequestHeaders(requestHeaders);
			page.setResponseHeaders(parser.getResponseHeader());
			page.setDeflated(parser.isDeflated());
			page.setChunked(parser.isChunked());
			page.setCharset(parser.getCharset());
			page.setContentEncoding(parser.getContentEncoding());
			page.setContentType(parser.getContentType());
			page.setOriginalDeflatedLength(parser.defaltedLength());
			HtmlEngine engine = new HtmlEngine();
			String result = null;
			try {
				result = engine.parser(html, options, parserURL);
			} catch (MinifierException e) {
				//log.severe("JS engine minify html failed. Error:" + e.getMessage());
				dispatchToErrorPage(req, resp,"Some errors occured while minify the content of url \"" + url + "\".<br> " + prettyError(e.getMessage()));
			}
			if(result != null){
				page.setMinifiedContent(new Text(result));
				page.setMinifierDefaltedLength(Util.defaltedLength(result,parser.getCharset()));
				WebContent con = WebContentUtil.insertContent(content);
				resp.sendRedirect("/display?key="+con.getId());
			}
		}else{
			dispatchToErrorPage(req, resp,"Could not fetch the content of url \"" + url + "\".");	  
		}
	}
	
	private void dispatchToErrorPage(HttpServletRequest req, HttpServletResponse resp,String error) throws ServletException, IOException{
		req.setAttribute("errors",error);
    	//dispatch to error.jsp;
    	RequestDispatcher dispatcher =
    	      req.getRequestDispatcher("error.jsp");
    	dispatcher.forward(req, resp);
	}
	
	private String prettyError(String message){
		String val = message.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;");
		val = val.replaceFirst(".*org\\.mozilla\\.javascript\\.JavaScriptException:", "");
		return val.replaceAll("\\(.*engine.js.*\\)","");
		
	}
}
